www.gusucode.com > VC++ P2P下载软件源代码-源码程序 > VC++ P2P下载软件源代码-源码程序\code\client\Client.cpp

    //Download by http://www.NewXing.com
/* 
 * Copyright (C) 2001-2003 Jacek Sieka, j_s@telia.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "stdinc.h"
#include "DCPlusPlus.h"

#include "Socket.h"
#include "Client.h"
#include "ClientManager.h"
#include "SearchManager.h"
#include "ShareManager.h"

Client::Counts Client::counts;

Client::~Client() throw() {
	TimerManager::getInstance()->removeListener(this);
	socket->removeListener(this);
	removeListeners();

	clearUsers();
	updateCounts(true);

	BufferedSocket::putSocket(socket);
	socket = NULL;
};

void Client::updateCounts(bool aRemove) {
	// We always remove the count and then add the correct one if requested...

	if(countType == COUNT_NORMAL) {
		Thread::safeDec(&counts.normal);
	} else if(countType == COUNT_REGISTERED) {
		Thread::safeDec(&counts.registered);
	} else if(countType == COUNT_OP) {
		Thread::safeDec(&counts.op);
	}
	countType = COUNT_UNCOUNTED;

	if(!aRemove) {
		if(op) {
			Thread::safeInc(&counts.op);
			countType = COUNT_OP;
		} else if(registered) {
			Thread::safeInc(&counts.registered);
			countType = COUNT_REGISTERED;
		} else {
			Thread::safeInc(&counts.normal);
			countType = COUNT_NORMAL;
		}
	}
}

void Client::connect(const string& aServer) {
	string tmp;
	port = 411;
	Util::decodeUrl(aServer, server, port, tmp);

	connect();
}

void Client::connect() {
	op = false;
	registered = false;
	reconnect = true;

	if(socket->isConnected()) {
		disconnect();
	}

	state = STATE_LOCK;

	socket->connect(server, port);
}

void Client::refreshUserList(bool unknownOnly /* = false */) {
	Lock l(cs);
	if(unknownOnly) {
		for(User::NickIter i = users.begin(); i != users.end(); ++i) {
			if(i->second->getConnection().empty()) {
				getInfo(i->second);
			}
		}
	} else {
		clearUsers();
		getNickList();
	}
}

void Client::clearUsers() {
	for(User::NickIter i = users.begin(); i != users.end(); ++i) {
		ClientManager::getInstance()->putUserOffline(i->second);		
	}
	users.clear();
}

void Client::onLine(const string& aLine) throw() {
	lastActivity = GET_TICK();

	if(aLine.length() == 0)
		return;
	
	if(aLine[0] != '$') {
		// Check if we're being banned...
		if(state != STATE_CONNECTED) {
			if(Util::findSubString(aLine, "banned") != string::npos) {
				reconnect = false;
			}
		}
		fire(ClientListener::MESSAGE, this, Util::validateMessage(aLine, true));
		return;
	}

	string cmd;
	string param;
	string::size_type x;
	
	if( (x = aLine.find(' ')) == string::npos) {
		cmd = aLine;
	} else {
		cmd = aLine.substr(0, x);
		param = aLine.substr(x+1);
	}

	if(cmd == "$Search") {
		if(state != STATE_CONNECTED) {
			return;
		}
		string::size_type i = 0;
		string::size_type j = param.find(' ', i);
		if(j == string::npos || i == j)
			return;
		string seeker = param.substr(i, j-i);
		i = j + 1;
		
		{
			Lock l(cs);
			u_int32_t tick = GET_TICK();

			seekers.push_back(make_pair(seeker, tick));

			// First, check if it's a flooder
			FloodIter fi;
			for(fi = flooders.begin(); fi != flooders.end(); ++fi) {
				if(fi->first == seeker) {
					return;
				}
			}

			int count = 0;
			for(fi = seekers.begin(); fi != seekers.end(); ++fi) {
				if(fi->first == seeker)
					count++;

				if(count > 7) {
					if(seeker.find("Hub:") != string::npos)
						fire(ClientListener::SEARCH_FLOOD, this, seeker.substr(4));
					else
						fire(ClientListener::SEARCH_FLOOD, this, seeker + STRING(NICK_UNKNOWN));

					flooders.push_back(make_pair(seeker, tick));
					return;
				}
			}
		}

		int a;
		if(param[i] == 'F') {
			a = SearchManager::SIZE_DONTCARE;
		} else if(param[i+2] == 'F') {
			a = SearchManager::SIZE_ATLEAST;
		} else {
			a = SearchManager::SIZE_ATMOST;
		}
		i += 4;
		j = param.find('?', i);
		if(j == string::npos || i == j)
			return;
		string size = param.substr(i, j-i);
		i = j + 1;
		j = param.find('?', i);
		if(j == string::npos || i == j)
			return;
		int type = Util::toInt(param.substr(i, j-i)) - 1;
		i = j + 1;
		param = param.substr(i);

		if(param.size() > 0) {
			fire(ClientListener::SEARCH, this, seeker, a, size, type, param);
			
			if(seeker.find("Hub:") != string::npos) {
				User::Ptr u;
				{
					Lock l(cs);
					User::NickIter ni = users.find(seeker.substr(4));
					if(ni != users.end() && !ni->second->isSet(User::PASSIVE)) {
						u = ni->second;
						u->setFlag(User::PASSIVE);
					}
				}

				if(u) {
					updated(u);
				}
			}
		}
	} else if(cmd == "$MyINFO") {
		string::size_type i, j;
		i = 5;
		string nick;
		j = param.find(' ', i);
		if( (j == string::npos) || (j == i) )
			return;
		nick = param.substr(i, j-i);
		i = j + 1;
		User::Ptr u;
		dcassert(nick.size() > 0);

		{
			Lock l(cs);
			User::NickIter ni = users.find(nick);
			if(ni == users.end()) {
				u = ClientManager::getInstance()->getUser(nick, this);
				users[nick] = u;
			} else {
				u  = ni->second;
			}
		}
		j = param.find('$', i);
		if(j == string::npos)
			return;
		string tmpDesc = Util::validateMessage(param.substr(i, j-i), true);
		// Look for a tag...
		if(tmpDesc.size() > 0 && tmpDesc[tmpDesc.size()-1] == '>') {
			string::size_type x = tmpDesc.rfind('<');
			if(x != string::npos) {
				// Hm, we have something...
				u->setTag(tmpDesc.substr(x));
				tmpDesc.erase(x);
			}
		}
		u->setDescription(tmpDesc);
		i = j + 3;
		j = param.find('$', i);
		if(j == string::npos)
			return;
		u->setConnection(param.substr(i, j-i-1));
		i = j + 1;
		j = param.find('$', i);
		if(j == string::npos)
			return;
		u->setEmail(Util::validateMessage(param.substr(i, j-i), true));
		i = j + 1;
		j = param.find('$', i);
		if(j == string::npos)
			return;
		u->setBytesShared(param.substr(i, j-i));
		
		fire(ClientListener::MY_INFO, this, u);
	} else if(cmd == "$Quit") {
		if(!param.empty()) {
			User::Ptr u;
			{
				Lock l(cs);
				User::NickIter i = users.find(param);
				if(i == users.end()) {
					dcdebug("C::onLine Quitting user %s not found\n", param.c_str());
					return;
				}
				
				u = i->second;
				users.erase(i);
			}
			
			fire(ClientListener::QUIT, this, u);
			ClientManager::getInstance()->putUserOffline(u, true);
		}
	} else if(cmd == "$ConnectToMe") {
		if(state != STATE_CONNECTED) {
			return;
		}
		string::size_type i = param.find(' ');
		string::size_type j;
		if( (i == string::npos) || ((i + 1) >= param.size()) ) {
			return;
		}
		i++;
		j = param.find(':', i);
		if(j == string::npos) {
			return;
		}
		string server = param.substr(i, j-i);
		if(j+1 >= param.size()) {
			return;
		}
		fire(ClientListener::CONNECT_TO_ME, this, server, param.substr(j+1));

	} else if(cmd == "$RevConnectToMe") {
		if(state != STATE_CONNECTED) {
			return;
		}
		User::Ptr u;
		bool up = false;
		{
			Lock l(cs);
			string::size_type j = param.find(' ');
			if(j == string::npos) {
				return;
			}

			User::NickIter i = users.find(param.substr(0, j));
			if(i == users.end()) {
				return;
			}

			u = i->second;
			if(!u->isSet(User::PASSIVE)) {
				u->setFlag(User::PASSIVE);
				up = true;
			}
		}

		if(u) {
			if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_ACTIVE) {
				fire(ClientListener::REV_CONNECT_TO_ME, this, u);
			} else {
				// Notify the user that we're passive too...
				if(up)
					revConnectToMe(u);
			}

			if(up)
				updated(u);
		}
	} else if(cmd == "$SR") {
		SearchManager::getInstance()->onSearchResult(aLine);
	} else if(cmd == "$HubName") {
		name = param;
		fire(ClientListener::HUB_NAME, this);
	} else if(cmd == "$Lock") {
		if(state != STATE_LOCK) {
			return;
		}
		state = STATE_HELLO;

		if(!param.empty()) {
			string::size_type j = param.find(" Pk=");
			string lock, pk;
			if( j != string::npos ) {
				lock = param.substr(0, j);
				pk = param.substr(j + 4);
			} else {
				// Workaround for faulty linux hubs...
				j = param.find(" ");
				if(j != string::npos)
					lock = param.substr(0, j);
				else
					lock = param;
			}
			fire(ClientListener::C_LOCK, this, lock, pk);	
		}
	} else if(cmd == "$Hello") {
		if(!param.empty()) {
			User::Ptr& u = ClientManager::getInstance()->getUser(param, this);
			{
				Lock l(cs);
				users[param] = u;
			}
			
			if(u->getNick() == getNick()) {
				if(state == STATE_HELLO) {
					state = STATE_CONNECTED;
					updateCounts(false);

					u->setFlag(User::DCPLUSPLUS);
					if(SETTING(CONNECTION_TYPE) != SettingsManager::CONNECTION_ACTIVE)
						u->setFlag(User::PASSIVE);
				}
			}
			
			fire(ClientListener::HELLO, this, u);
		}
	} else if(cmd == "$ForceMove") {
		disconnect();
		fire(ClientListener::FORCE_MOVE, this, param);
	} else if(cmd == "$HubIsFull") {
		fire(ClientListener::HUB_FULL, this);
	} else if(cmd == "$ValidateDenide") {		// Mind the spelling...
		disconnect();
		fire(ClientListener::VALIDATE_DENIED, this);
	} else if(cmd == "$NickList") {
		if(!param.empty()) {
			User::List v;

			string::size_type j, k = 0;
			{
				Lock l(cs);
				while( (j=param.find("$$", k)) != string::npos) {
					if(j == k) {
						k += 2;
						continue;
					}
					string nick = param.substr(k, j-k);
					User::Ptr& u = ClientManager::getInstance()->getUser(nick, this);

					users[nick] = u;

					v.push_back(u);
					k = j + 2;
				}
			}
			
			fire(ClientListener::NICK_LIST, this, v);
		}
	} else if(cmd == "$OpList") {
		if(!param.empty()) {
			User::List v;
			string::size_type j, k;
			k = 0;
			{
				Lock l(cs);
				while( (j=param.find("$$", k)) != string::npos) {
					if(j == k) {
						k += 2;
						continue;
					}
					string nick = param.substr(k, j-k);
					User::Ptr& u = ClientManager::getInstance()->getUser(nick, this);
					users[nick] = u;
					u->setFlag(User::OP);

					v.push_back(u);
					k = j + 2;
				}
			}
			fire(ClientListener::OP_LIST, this, v);
			updateCounts(false);
			// Special...to avoid op's complaining that their count is not correctly
			// updated when they log in (they'll be counted as registered first...)
			if(lastCounts != counts) {
				myInfo();
			}
		}
	} else if(cmd == "$To:") {
		string::size_type i = param.find("From:");
		if(i != string::npos) {
			i+=6;
			string::size_type j = param.find("$");
			if(j != string::npos) {
				string from = param.substr(i, j - 1 - i);
				if(from.size() > 0 && param.size() > (j + 1)) {
					fire(ClientListener::PRIVATE_MESSAGE, this, ClientManager::getInstance()->getUser(from, this, false), Util::validateMessage(param.substr(j + 1), true));
				}
			}
		}
	} else if(cmd == "$GetPass") {
		registered = true;
		fire(ClientListener::GET_PASSWORD, this);
	} else if(cmd == "$BadPass") {
		fire(ClientListener::BAD_PASSWORD, this);
	} else if(cmd == "$LogedIn") {
		fire(ClientListener::LOGGED_IN, this);
	} else {
		dcassert(cmd[0] == '$');
		dcdebug("Client::onLine Unknown command %s\n", aLine.c_str());
	} 
}

void Client::myInfo() {
	checkstate();
	
	dcdebug("MyInfo %s...\n", getNick().c_str());
	lastCounts = counts;
	
	string tmp1 = ";**\x1fU9";
	string tmp2 = "+L9";
	string tmp3 = "+G9";
	string tmp4 = "+R9";
	string tmp5 = "+N9";
	string::size_type i;
	
	for(i = 0; i < 6; i++) {
		tmp1[i]++;
	}
	for(i = 0; i < 3; i++) {
		tmp2[i]++; tmp3[i]++; tmp4[i]++; tmp5[i]++;
	}
	char modeChar = '?';
	if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_ACTIVE)
		modeChar = 'A';
	else if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_PASSIVE)
		modeChar = 'P';
	else if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_SOCKS5)
		modeChar = '5';
	
	string uMin = (SETTING(MIN_UPLOAD_SPEED) == 0) ? Util::emptyString : tmp5 + Util::toString(SETTING(MIN_UPLOAD_SPEED));
	send("$MyINFO $ALL " + Util::validateNick(getNick()) + " " + Util::validateMessage(getDescription(), false) + 
		tmp1 + VERSIONSTRING + tmp2 + modeChar + tmp3 + getCounts() + tmp4 + Util::toString(SETTING(SLOTS)) + uMin + 
		">$ $" + SETTING(CONNECTION) + "\x01$" + Util::validateMessage(SETTING(EMAIL), false) + '$' + 
		ShareManager::getInstance()->getShareSizeString() + "$|");
}

void Client::disconnect() throw() {	
	state = STATE_CONNECT;
	socket->disconnect();
	{ 
		Lock l(cs);
		clearUsers();
	}
}

void Client::search(int aSizeType, int64_t aSize, int aFileType, const string& aString){
	checkstate(); 
	char* buf;
	char c1 = (aSizeType == SearchManager::SIZE_DONTCARE) ? 'F' : 'T';
	char c2 = (aSizeType == SearchManager::SIZE_ATLEAST) ? 'F' : 'T';
	string tmp = aString;
	string::size_type i;
	while((i = tmp.find(' ')) != string::npos) {
		tmp[i] = '$';
	}
	int chars = 0;
	if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_ACTIVE) {
		string x = getLocalIp();
		buf = new char[x.length() + aString.length() + 64];
		chars = sprintf(buf, "$Search %s:%d %c?%c?%s?%d?%s|", x.c_str(), SETTING(IN_PORT), c1, c2, Util::toString(aSize).c_str(), aFileType+1, tmp.c_str());
	} else {
		buf = new char[getNick().length() + aString.length() + 64];
		chars = sprintf(buf, "$Search Hub:%s %c?%c?%s?%d?%s|", getNick().c_str(), c1, c2, Util::toString(aSize).c_str(), aFileType+1, tmp.c_str());
	}
	send(buf, chars);
	delete[] buf;
}

void Client::kick(const User::Ptr& aUser, const string& aMsg) {
	checkstate(); 
	dcdebug("Client::kick\n");
	static const char str[] = 
		"$To: %s From: %s $<%s> You are being kicked because: %s|<%s> %s is kicking %s because: %s|";
	string msg2 = Util::validateMessage(aMsg, false);
	
	char* tmp = new char[sizeof(str) + 2*aUser->getNick().length() + 2*msg2.length() + 4*getNick().length()];
	const char* u = aUser->getNick().c_str();
	const char* n = getNick().c_str();
	const char* m = msg2.c_str();
	sprintf(tmp, str, u, n, n, m, n, n, u, m);
	send(tmp);
	delete[] tmp;
	
	// Short, short break to allow the message to reach the client...
	Thread::sleep(200);
	send("$Kick " + aUser->getNick() + "|");
}

void Client::kick(User* aUser, const string& aMsg) {
	checkstate(); 
	dcdebug("Client::kick\n");
	
	static const char str[] = 
		"$To: %s From: %s $<%s> You are being kicked because: %s|<%s> %s is kicking %s because: %s|";
	string msg2 = Util::validateMessage(aMsg, false);
	
	char* tmp = new char[sizeof(str) + 2*aUser->getNick().length() + 2*msg2.length() + 4*getNick().length()];
	const char* u = aUser->getNick().c_str();
	const char* n = getNick().c_str();
	const char* m = msg2.c_str();
	sprintf(tmp, str, u, n, n, m, n, n, u, m);
	send(tmp);
	delete[] tmp;
	
	// Short, short break to allow the message to reach the client...
	Thread::sleep(100);
	send("$Kick " + aUser->getNick() + "|");
}

// TimerManagerListener
void Client::onAction(TimerManagerListener::Types type, u_int32_t aTick) throw() {
	if(type == TimerManagerListener::SECOND) {
		if(socket && (lastActivity + 120 * 1000) < aTick) {
			// Nothing's happened for 120 seconds, check if we're connected, if not, try to connect...
			lastActivity = aTick;
			// Try to send something for the fun of it...
			if(isConnected()) {
				dcdebug("Testing writing...\n");
				socket->write("|", 1);
			} else {
				// Try to reconnect...
				if(reconnect && !server.empty())
					connect();
			}
		}
		{
			Lock l(cs);
			
			while(!seekers.empty() && seekers.front().second + (5 * 1000) < aTick) {
				seekers.pop_front();
			}
			
			while(!flooders.empty() && flooders.front().second + (120 * 1000) < aTick) {
				flooders.pop_front();
			}
		}
	} 
}

// BufferedSocketListener
void Client::onAction(BufferedSocketListener::Types type, const string& aLine) throw() {
	switch(type) {
	case BufferedSocketListener::LINE:
		onLine(aLine); break;
	case BufferedSocketListener::FAILED:
		{
			Lock l(cs);
			clearUsers();
		}
		if(state == STATE_CONNECTED)
			state = STATE_CONNECT;
		fire(ClientListener::FAILED, this, aLine); break;
	default:
		dcassert(0);
	}
}

void Client::onAction(BufferedSocketListener::Types type) throw() {
	switch(type) {
	case BufferedSocketListener::CONNECTING:
		fire(ClientListener::CONNECTING, this); break;
	case BufferedSocketListener::CONNECTED:
		lastActivity = GET_TICK();
		fire(ClientListener::CONNECTED, this);
		break;
	default:
		break;
	}
}

/**
 * @file
 * $Id: Client.cpp,v 1.53 2003/07/15 14:53:10 arnetheduck Exp $
 */